import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:im_demo/app/modules/login/views/login_view.dart';

import '../../app/constant/storage_key.dart';
import '../../utils/logger_util.dart';
import 'http_config.dart';

class HttpUtil {
  static final HttpUtil _instace = HttpUtil._internal();

  late Dio _dio;

  // Dio get dio => _dio;

  CancelToken _globalCancelToken = CancelToken();
  String _defaultBaseUrl;

  // String _token;
  factory HttpUtil() => HttpUtil._instace;

  HttpUtil._internal() : _defaultBaseUrl = HttpConfig.baseUrl1;

  void init({
    required String defaultBaseUrl,
    // String? token,
    int connectTimeout = HttpConfig.connectTimeout,
    int requestTimeout = HttpConfig.requestTimeout,
    int responseTimeout = HttpConfig.responseTimeout,
    bool debugMode = false,
  }) {
    _defaultBaseUrl = defaultBaseUrl;

    BaseOptions baseOptions = BaseOptions(
      connectTimeout: Duration(milliseconds: connectTimeout),
      sendTimeout: Duration(milliseconds: requestTimeout),
      receiveTimeout: Duration(milliseconds: responseTimeout),
      headers: {
        // HttpHeaders.acceptHeader:"application/json",
        HttpHeaders.acceptHeader: Headers.jsonContentType,
        HttpHeaders.userAgentHeader: 'im_http',
      },
      contentType: Headers.jsonContentType,
      responseType: ResponseType.json,
    );

    _dio = Dio(baseOptions);

    if (debugMode) {
      _dio.interceptors.add(_logInterceptor());

      final bool enableProxy =
          GetStorage().read(StorageKey.ENABLE_PROXY) ?? false;
      logE('init()-->enableProxy:$enableProxy');

      if (enableProxy) {
        final String proxyAddress =
            GetStorage().read(StorageKey.PROXY_ADDRESS) ?? '';
        logE('init()-->proxyAddress:$proxyAddress');

        (_dio.httpClientAdapter as DefaultHttpClientAdapter)
            .onHttpClientCreate = (client) {
          client.findProxy = (uri) {
            //TODO 使用时替换成代理ip
            // return 'PROXY localhost:8888';
            return 'PROXY $proxyAddress';
          };
          //忽略证书
          client.badCertificateCallback =
              (X509Certificate cert, String host, int port) => true;
        };
      }
    }
  }

  LogInterceptor _logInterceptor() {
    return LogInterceptor(
      request: true,
      requestHeader: true,
      requestBody: true,
      responseHeader: true,
      responseBody: true,
      error: true,
      logPrint: (object) {
        logI(object);
      },
    );
  }

  String _resolveUrl(String? baseUrl, String path) {
    final uri =
        baseUrl != null
            ? Uri.parse(baseUrl).resolve(path)
            : Uri.parse(_defaultBaseUrl).resolve(path);
    return uri.toString();
  }

  Map<String, dynamic> _mergeHeaders(Map<String, dynamic>? headers) {
    final token = GetStorage().read(StorageKey.TOKEN);
    final defaultHeaders = {
      if (token != null) 'Authorization': 'Bearer $token',
    };
    return {...defaultHeaders, ...?headers};
  }

  HttpError _handleError(DioException error) {
    final statusCode = error.response!.statusCode;
    logE('_handleError()-->statusCode:$statusCode');

    final serverMessage = error.response?.data?['message'] ?? '请求失败';

    switch (error.type) {
      case DioExceptionType.connectionTimeout:
        return HttpError(message: '连接超时');
      case DioExceptionType.receiveTimeout:
        return HttpError(message: '接收超时');
      case DioExceptionType.sendTimeout:
        return HttpError(message: '发送超时');
      case DioExceptionType.cancel:
        return HttpError(message: '请求取消');
      case DioExceptionType.badResponse:
        if (statusCode == 401) {
          //TODO
          //to login
          Get.offAll(LoginView());
        }
        return HttpError(code: error.response?.statusCode, message: '响应出错');
      default:
        if (error.response != null) {
          return HttpError(
            code: statusCode,
            message: '[$statusCode]$serverMessage',
          );
        } else {
          return HttpError(message: '未知网络错误');
        }
    }
  }

  Future<HttpResult<T>> get<T>(
    String path, {
    String? baseUrl,
    Map<String, dynamic>? headers,
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await _dio.get<T>(
        _resolveUrl(baseUrl, path),
        queryParameters: params,
        options: Options(headers: _mergeHeaders(headers)),
        cancelToken: cancelToken ?? _globalCancelToken,
      );
      return HttpResult.success(response.data as T);
    } on DioException catch (e) {
      return HttpResult.failure(_handleError(e));
    }
  }

  Future<HttpResult<T>> post<T>(
    String path, {
    String? baseUrl,
    dynamic data,
    Map<String, dynamic>? headers,
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await _dio.post<T>(
        _resolveUrl(baseUrl, path),
        data: data,
        queryParameters: params,
        options: Options(headers: _mergeHeaders(headers)),
        cancelToken: cancelToken ?? _globalCancelToken,
      );
      logE('post()-->response:$response,type:${response.runtimeType}');
      return HttpResult.success(response.data as T);
    } on DioException catch (e) {
      return HttpResult.failure(_handleError(e));
    }
  }

  Future<HttpResult<T>> put<T>(
    String path, {
    String? baseUrl,
    dynamic data,
    Map<String, dynamic>? headers,
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await _dio.put<T>(
        _resolveUrl(baseUrl, path),
        data: data,
        queryParameters: params,
        options: Options(headers: _mergeHeaders(headers)),
        cancelToken: cancelToken ?? _globalCancelToken,
      );
      return HttpResult.success(response.data as T);
    } on DioException catch (e) {
      return HttpResult.failure(_handleError(e));
    }
  }

  Future<HttpResult<T>> delete<T>(
    String path, {
    String? baseUrl,
    dynamic data,
    Map<String, dynamic>? headers,
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
  }) async {
    try {
      final response = await _dio.delete<T>(
        _resolveUrl(baseUrl, path),
        data: data,
        queryParameters: params,
        options: Options(headers: _mergeHeaders(headers)),
        cancelToken: cancelToken ?? _globalCancelToken,
      );
      return HttpResult.success(response.data as T);
    } on DioException catch (e) {
      return HttpResult.failure(_handleError(e));
    }
  }

  void cancelAllRequests({String? message}) {
    _globalCancelToken.cancel(message ?? '请求取消');
    _globalCancelToken = CancelToken(); // 重置 Token
  }
}

class HttpResult<T> {
  final T? data;
  final HttpError? error;

  HttpResult._({this.data, this.error});

  factory HttpResult.success(T data) => HttpResult._(data: data);

  factory HttpResult.failure(HttpError error) => HttpResult._(error: error);

  bool get isSuccess => error == null;
}

class HttpError {
  final int? code;
  final String message;

  HttpError({this.code, required this.message});
}
